package com.ledou44.ledourpc.client;

import com.ledou44.ledourpc.clienthandler.*;
import com.ledou44.ledourpc.dto.LedouRpcDTO;
import com.ledou44.ledourpc.dto.LedouSradDTO;
import com.ledou44.ledourpc.dto.LedousradMessage;
import com.ledou44.ledourpc.dto.ServiceDTO;
import com.ledou44.ledourpc.service.Service;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

@Slf4j
@Component
public class Client {

    @Autowired
    private LedousradMessage ledousradMessage;

    @Autowired
    private Service service;

    /**
     * 进行服务远程调用client
     * @param message 消息
     */
    public void run(LedouRpcDTO message, String address, Integer port) {
        ChannelHandlerAdapter channelHandler = new DefaultClientHandler(message);
        doRun(address, port, channelHandler);
    }

    /**
     * 进行服务注册client
     * @param message 消息
     */
    public void registerService(LedouSradDTO message) {
        List<ServiceDTO> serviceList = message.getServiceList();
        for(ServiceDTO serviceDTO : serviceList) {
            serviceDTO.setPort(service.getServicePort());
        }
        String address = ledousradMessage.getAddress();
        Integer port = ledousradMessage.getPort();
        ChannelHandlerAdapter channelHandler = new RegisterClientHandler(message);
        ClientHandler clientHandler = new ClientHandler(channelHandler, 1);

        EventLoopGroup work = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(work)
                .channel(NioSocketChannel.class)
                //设置连接超时时间
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .handler(clientHandler);
        try {
            bootstrap.connect(address, port).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 与服务注册与发现中心通讯，获取调用服务的地址
     * @param message 消息
     */
    public void serviceDiscovery(LedouSradDTO message) {
        String address = ledousradMessage.getAddress();
        Integer port = ledousradMessage.getPort();
        ChannelHandlerAdapter channelHandler = new DiscoveryClientHandler(message);
        doRun(address, port, channelHandler);
    }

    /**
     * 通用netty方法，作为netty-clint端发生一次tcp通信，与netty-service端交互数据
     * 在服务注册，服务发现，每次调用方法进行RPC交互时都会触发该方法
     * 此方法即用即停，拿到返回结果后就会挂断，不像ServiceStart里面启动的netty-service端会持续监听消息不挂断
     * @param address 调用ip地址
     * @param port 调用端口
     * @param channelHandler
     */
    private void doRun(String address, Integer port, ChannelHandlerAdapter channelHandler) {
        ClientHandler clientHandler = new ClientHandler(channelHandler, 0);

        EventLoopGroup work = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(work)
                .channel(NioSocketChannel.class)
                //设置连接超时时间
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .handler(clientHandler);
        try {
            ChannelFuture future = bootstrap.connect(address, port).sync();
            //等待客户端断开连接请求，不加主线程会继续走下去
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
